#include "util.hpp"
#include <pthread.h>
using namespace std;


// ctrl k + ctrl f
class tcpServe
{
public:
    class threadData
    {
    public:
        threadData(int sock, string ip, uint16_t port, tcpServe* ts)
            :_sock(sock), _ip(ip), _port(port), _ts(ts)
        {}
    public:
        int _sock;
        string _ip;
        uint16_t _port;
        tcpServe *_ts;
    };
    tcpServe(uint16_t port, string ip = "")
        : _sock(-1), _port(port), _ip(ip)
    {}
    ~tcpServe()
    {}
    void init()
    {
        // 1. 建立套接字
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if(_sock < 0)
        {
            logMessage(FATAL, "socket create fail");
            exit(SOCK_FATAL);
        }

        // 2.1 填充基本信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET; // PF_INET   PF_INET == AF_INET
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = _ip.empty() ? htonl(INADDR_ANY) : inet_addr(_ip.c_str());
        // _ip.empty() ? (local.sin_addr.s_addr = INADDR_ANY) : (inet_aton(_ip.c_str(), &local.sin_addr));
        // 2.2 本地sock信息 写入对应的内核区域
        if (bind(_sock, (const struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "socket bind fail");
            exit(BIND_FAIL);
        }

        // 3 监听端口
        if(listen(_sock, 5) < 0)
        {
            logMessage(FATAL, "socket listen fail");
            exit(LISTEN_FAIL);
        }
        logMessage(DEBUG, "socket init success");
    }
    void loop()
    {
        while(true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            // 4. 获取和用户的连接
            int serveSock = accept(_sock, (struct sockaddr *)&peer, &len);
            if(serveSock < 0)
            {
                logMessage(WARNINING, "%d accept fail", _sock);
                continue;
            }
            uint16_t peerPort = ntohs(peer.sin_port);
            string peerIp = inet_ntoa(peer.sin_addr);
            logMessage(DEBUG, "serve %d accept %s[%d]", _sock, peerIp.c_str(), peerPort);
            // 由于是单进程 多个用户无法同时连接
            // 解决方法
            // 1 多进程 问题: 要等待子进程 
            // 解决方法 : 
            // 1. 忽略子进程结束后返回的SIGCLD信号 让系统处理 (仅限LINUX)
            // 2. 利用孤儿进程会被系统托管的特点 创建孙子进程 并让父亲进程退出 使得孙子进程为孤儿进程
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     // 爸爸进程
            //     close(_sock);//建议
            //     // 又进行了一次fork，让 爸爸进程
            //     if(fork() > 0) exit(0);  // 爸爸进程退出
            //     // 孙子进程 -- 就没有爸爸 -- 孤儿进程 -- 被系统领养 -- 回收问题就交给了系统来回收
            //     transService(serviceSock, peerIp, peerPort);
            //     exit(0);
            // }
            // // 父进程
            // close(serviceSock); //这一步是一定要做的！
            // // 爸爸进程直接终止，立马得到退出码，释放僵尸进程状态
            // pid_t ret = waitpid(id, nullptr, 0); //就用阻塞式
            // assert(ret > 0);
            // (void)ret;

            // 2. 多线程
            // 1. 普通多线程 detach 2. 线程池
            threadData *data = new threadData(serveSock, peerIp, peerPort, this);
            pthread_t pd;
            pthread_create(&pd, nullptr, threadRouter, (void*)data);
        }
    }
    static void* threadRouter(void* arg)
    {
        if(arg == nullptr)
        {
            logMessage(WARNINING, "arg is nullptr");
            return nullptr;
        }

        threadData *data = (threadData *)arg;
        data->_ts->transformServe(data->_sock, data->_ip, data->_port);
        return nullptr;
    }
    // 这里传入的sock为用户连接的sock
    void transformServe(int sock, const string& ip, uint16_t port)
    {
        assert(sock > 0);
        assert(!ip.empty());
        assert(port > 1023);
        char inBuff[BUFF_SIZE] = { 0 };
        while(true)
        {
            ssize_t s = read(sock, inBuff, sizeof(inBuff) - 1);
            if(s >0)
            {
                inBuff[s] = '\0';  // 0
                if(strcasecmp(inBuff, "quit") == 0) // 不区分大小写的比较
                {
                    logMessage(DEBUG, "client quit -- %s[%d]", ip.c_str(), port);
                    break;
                }
                logMessage(DEBUG, "trans before: %s[%d]>>> %s", ip.c_str(), port, inBuff);
                for (int i = 0; i < s; ++i)
                {
                    if(isalpha(inBuff[i]) && islower(inBuff[i])) 
                        inBuff[i] = toupper(inBuff[i]);
                }
                logMessage(DEBUG, "trans after: %s[%d]>>> %s", ip.c_str(), port, inBuff);
                write(sock, inBuff, strlen(inBuff) + 1);
            }
            else if (s == 0)  // 代表没有读到数据 表明对方关闭
            {
                // pipe: 读端一直在读，写端不写了，并且关闭了写端，读端会如何？s == 0，代表对端关闭
                // s == 0: 代表对方关闭,client 退出
                logMessage(DEBUG, "client quit -- %s[%d]", ip.c_str(), port);
                break;
            }
            else
            {
                // 表明读操作出错
                logMessage(DEBUG, "%s[%d] - read: %s", ip.c_str(), port, strerror(errno));
                break;
            }
        }
        
    }
private:
    int _sock;
    string _ip;
    uint16_t _port;
};


static void usage(char* process)
{
    cout << process << " port ip" << endl;
    cout << "example: " << process << " 8080 127.0.0.1" << endl;
}

int main(int argc, char* argv[])
{
    if(argc != 2 && argc != 3)
    {
        usage(argv[0]);
        exit(0);
    }
    tcpServe serve(atoi(argv[1]));
    serve.init();
    serve.loop();

    return 0;
}